import { Injectable, Injector } from '@angular/core';
import { Router, NavigationStart } from '@angular/router';
import { filter } from 'rxjs/operators';

import { StoreService } from './store.service';
import { LoggerService } from './logger.service';
import { isString } from '../../util';

@Injectable({
  providedIn: 'root'
})
export class AclService<B, S> {

  // 全字匹配的acl
  private aclMap: Map<string, Acl>;
  // 前置模糊匹配的acl
  private aclPrefix: Acl[];
  private isWatch = false;

  constructor(
    private storeService: StoreService<B, S>,
    private injector: Injector,
    private loggerService: LoggerService
  ) { }

  // 初始化acl信息，参数为原始acl数据，如果为空则从localStorage中读取
  init(aclList?: Acl[]) {
    if (!aclList) {
      aclList = this.storeService.getItem(<any>'Acl') || [];
    } else {
      this.storeService.setItem(<any>'Acl', aclList);
    }

    this.aclMap = new Map<string, any>();
    this.aclMap.set('/', { url: '/', name: 'home' });
    this.aclMap.set('/404', { url: '/404', name: 'not found' });
    this.aclMap.set('/403', { url: '/403', name: 'forbidden' });
    this.aclMap.set('/entry', { url: '/entry', name: 'entry' });
    this.aclPrefix = [];

    aclList.forEach((acl) => {
      // 考虑非 / 开头的情况
      if (!(acl.url.startsWith('$') || acl.url.startsWith('/'))) {
        acl.url = '/' + acl.url;
      }

      if (acl.prefix) {
        this.aclPrefix.push(acl);
      } else {
        this.aclMap.set(acl.url, acl);
      }
    });
  }

  // 根据url获取对应acl
  getItem(url: string): Acl {
    if (!this.aclMap) {
      this.init();
    }

    url = isString(url) ? url.replace(/\?[\w\W]*$/, '') : '';
    return this.aclMap.get(url) || this.aclPrefix.find(acl => url.includes(acl.url));
  }

  // 监听路由变化，拦截非法的路由跳转
  interceptRoute(fn?: (url: string, acl?: Acl) => boolean) {
    if (this.isWatch) {
      return;
    }

    const router = this.injector.get(Router);
    this.isWatch = true;
    router.events.pipe(
      filter((e) => e instanceof NavigationStart)
    ).subscribe((e: NavigationStart) => {
      const acl = this.getItem(e.url);
      if (!acl) {
        if (fn && fn(e.url) === true) {
          return;
        } else {
          router.navigateByUrl('/404');
          this.loggerService.error(new Error('route not found'), 'AclService');
        }
      } else if (acl.disabled) {
        if (fn && fn(e.url, acl) === true) {
          return;
        } else {
          router.navigateByUrl('/403');
          this.loggerService.error(new Error('route not found'), 'AclService');
        }
      }
    });
  }

  includeAclCode(aclCode: string): boolean {
    const router = this.injector.get(Router);
    const acl = this.getItem(router.url);
    if (acl && acl.aclCodeList) {
      return aclCode.split(' ').some((name) => {
        return acl.aclCodeList.includes(name);
      });
    }

    return false;
  }

  // 废弃
  includeAclBtn = (...args) => this.includeAclCode.apply(this, args);
}

export interface Acl {
  name: string;
  url: string;
  prefix?: boolean;
  aclCodeList?: [string];
  disabled?: boolean;
}
